package cn.netuo.bumbersoll.helper.ble;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.bluetooth.le.BluetoothLeAdvertiser;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.support.annotation.RequiresApi;
import android.text.TextUtils;
import android.widget.Toast;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

import cn.netuo.bumbersoll.App;
import cn.netuo.bumbersoll.BizConstant;
import cn.netuo.bumbersoll.entity.Action;
import cn.netuo.bumbersoll.entity.MqttFromS;
import cn.netuo.bumbersoll.helper.ble.interf.BluetoothGattWriteCharacteristicsListener;
import cn.netuo.bumbersoll.helper.ble.service.BleService;
import cn.netuo.bumbersoll.interf.OnBlueToothConnectListener;
import cn.netuo.bumbersoll.util.BsUtil;
import lib.grasp.util.L;
import lib.grasp.util.NumberUtil;
import lib.grasp.util.PreferenceUtil;
import lib.grasp.util.TOAST;

/**
 * Created by JS_grasp on 2019/3/16.
 * 低功耗蓝牙
 *
 *
 * <p/>好文推荐:
 * <br/><a href = https://blog.csdn.net/qq_32115439/article/details/80643906>Android-低功耗蓝牙(BLE)-客户端(主机/中心设备)和服务端(从机/外围设备)<href/>
 * <br/><a href = https://www.jianshu.com/p/2fd90849d8e0>Android ble （蓝牙低功耗） 中的坑和技巧<href/>
 *
 */
public class BLEHelper implements BluetoothGattWriteCharacteristicsListener {

    private Activity    mActivity;
    private Handler     mHandler = new Handler();

    /** 目标蓝牙 */
    private String                          mTargetBtAddr;
    /** 蓝牙适配器 */
    private BluetoothAdapter                mBluetoothAdapter;
    /** 蓝牙设备(对端) */
    private BluetoothDevice                 mDevice;
    /** 蓝牙搜索回调 */
    private BluetoothAdapter.LeScanCallback mLeScanCallback = this::onScan;
    /** 蓝牙服务(自定义)实体类 */
    private BleService                      bleService;

    /** 写入结果监听 */
    private OnBlueToothConnectListener mListener;

    public void setListener(OnBlueToothConnectListener mListener) {
        this.mListener = mListener;
    }

    /** 蓝牙服务(自定义)实体类 */
    private Map<String, MqttFromS<Action>> mActions = new HashMap<>();

//    /** 待关电任务 */
//    private MqttFromS<Action> mOffUsbAction = null;

    /** 获取待关电action */
    public MqttFromS<Action>  getOffUsbAction() {
        Object o =  PreferenceUtil.getSerializableEntity(mActivity, BizConstant.OFF_USB_TASK_KEY);
        if(!(o instanceof MqttFromS)) return null;
        return (MqttFromS<Action>)o;
    }

    /** 存储待关电action */
    public void  saveOffUsbAction(MqttFromS<Action> actionMqttFromS) {
        PreferenceUtil.saveSerializableEntity(mActivity, BizConstant.OFF_USB_TASK_KEY, actionMqttFromS);
    }

    /** 设置待关电action, 无则生之, 有则延长 */
    public void setOffUsbAction(MqttFromS<Action> mOffUsbAction) {

        MqttFromS<Action> offUsbAction = getOffUsbAction();
        if(mOffUsbAction == null) {
            L.logOnly("清空关电任务");
            saveOffUsbAction(null);    // 置空
        }
        else if(offUsbAction != null){
            L.logOnly("延长时间 + " + BizConstant.POWER_ON_PERIOD/1000 + "秒");
            offUsbAction.targetTime += BizConstant.POWER_ON_PERIOD;            // 延长
            saveOffUsbAction(offUsbAction);
        }
        else{
            L.logOnly("定时关闭, " + BizConstant.POWER_ON_PERIOD/1000 + "秒后");
            offUsbAction = BsUtil.makeMeUsbOff(mOffUsbAction);                 // 赋值(定时关闭)
            saveOffUsbAction(offUsbAction);
        }
    }

    public BLEHelper(Activity activity) {
        this.mActivity = activity;
        init();
    }


    /**
     * 添加任务
     * 1. 打开蓝牙
     * 2. 搜索蓝牙
     * 3. 连接服务
     * 4. 配对连接蓝牙
     * 5. 发送指令
     */
    public void addAction(MqttFromS<Action> actionMqttFromS){
        synchronized(BLEHelper.class){
            if(actionMqttFromS == null) return;
            mActions.put(actionMqttFromS.requestId, actionMqttFromS);   // 先加入待操作列表

            if(mBluetoothAdapter == null){
                BluetoothManager bluetoothManager = (BluetoothManager) mActivity.getSystemService(Context.BLUETOOTH_SERVICE);
                if(bluetoothManager == null) return;
                mBluetoothAdapter = bluetoothManager.getAdapter();
            }

            if(!mBluetoothAdapter.isEnabled()){     // 蓝牙没开
                L.logOnly("蓝牙没开");
                initBleAdapter();
                return;
            }

            if(mDevice == null){                // 之前没有连接过指定设备
                L.logOnly("之前没有连接过指定设备");
                doScan(actionMqttFromS);
                return;
            }

            if(bleService.mConnectionState == BleService.STATE_DISCONNECTED){       // 之前没有连接过指定设备 或者 断开连接了
                L.logOnly("找到指定设备, 但是[没连接]或者[断开连接]了");
                bleService.connect(mDevice.getAddress());
                return;
            }
            if(bleService.mConnectionState == BleService.STATE_CONNECTING) {        // 正在连接
                L.logOnly("正在连接");
                return;
            }

            sendMsg(actionMqttFromS);
        }
    }

    /** 初始化(打开)蓝牙设备 */
    public void init(){
        registerReceiver();
        doBindService();
        initBleArg();
    }

    /** 初始化蓝牙参数 */
    private void initBleArg() {
        mTargetBtAddr = App.getApp().getTableMgr().getTbArg().queryByArgName(BizConstant.ARG_BLE_ADDR_KEY);        // 目标蓝牙addr
    }

    /** 注册广播监听(主要监听蓝牙的一些事情) */
    private void registerReceiver(){
        mActivity.registerReceiver(mBroadcastReceiver, makeIntentFilter()); // 注册广播
    }

    /** 绑定连接服务 */
    private void doBindService(){
        mActivity.bindService(new Intent(mActivity, BleService.class), mConn, Context.BIND_AUTO_CREATE); // 绑定服务
    }

    /** 绑定服务回调 */
    private ServiceConnection mConn = new ServiceConnection() {
        @SuppressLint("NewApi")
        @Override
        public void onServiceConnected(ComponentName arg0, IBinder service) {
            L.logOnly("\"连接服务\"连接");
            bleService = ((BleService.LocalBinder) service).getService(); // 获得服务本身
            bleService.setListener(BLEHelper.this);
            bleService.init();
        }

        /** 服务断开断开 */
        @Override
        public void onServiceDisconnected(ComponentName arg0) {
            L.logOnly("\"连接服务\"断开");
            bleService = null;
        }
    };

    /** 初始化蓝牙适配器 */
    @SuppressLint("NewApi")
    private void initBleAdapter() {
        L.logOnly("开始打开蓝牙");
        if(mBluetoothAdapter != null) {
            mBluetoothAdapter.enable(); // 打开蓝牙
            return;
        }

        final BluetoothManager bluetoothManager = (BluetoothManager) mActivity.getSystemService(Context.BLUETOOTH_SERVICE);
        mBluetoothAdapter = bluetoothManager.getAdapter();
        if(mBluetoothAdapter == null){
            TOAST.showShort(mActivity, "很遗憾, 您的设备不支持蓝牙!");
            return;
        }
        mBluetoothAdapter.enable();     // 打开蓝牙
    }

    /** 扫描 */
    private void doScan(MqttFromS<Action> actionMqttFromS){
        L.logOnly("开始扫描");
        mDevice = null;
        mBluetoothAdapter.stopLeScan(mLeScanCallback);

        // 这里可以开启扫描过滤(ScanFilter类), 参考 https://www.jianshu.com/p/2fd90849d8e0
        new Thread(() -> mBluetoothAdapter.startLeScan(mLeScanCallback)).start();
        mHandler.postDelayed(() -> {
            if(mBluetoothAdapter != null && mLeScanCallback != null) mBluetoothAdapter.stopLeScan(mLeScanCallback);
            if(mDevice == null){    // 没有搜到目标设备
                L.logOnly("没有搜到目标设备");
                mActions.remove(actionMqttFromS);
                if(!BizConstant.IS_BT_KEPP_ON) disableBT();
            }
        }, 1000 * 5);
    }

    /** 扫描回调 */
    private void onScan(BluetoothDevice device1, int rssi, byte[] scanRecord){
        L.logOnly("搜到设备:" + device1.getName() + ", Addr: " + device1.getAddress());
        if(!TextUtils.equals(mTargetBtAddr, device1.getAddress())) return;

        mBluetoothAdapter.stopLeScan(mLeScanCallback);
        mDevice = device1;           // 搜索指定名称的设备
        TOAST.showShort(mActivity, "成功找到目标蓝牙设备");

        checkIfDoNext();
    }


    /** 蓝牙事件广播(本人蓝牙服务发出的广播) */
    private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
        @SuppressLint("NewApi")
        @Override
        public void onReceive(Context arg0, Intent intent) {
            String action = intent.getAction();
            assert action != null;
            switch (action){
                case BluetoothAdapter.ACTION_STATE_CHANGED: {
                    int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
                    if(state == BluetoothAdapter.STATE_OFF) {
                        L.logOnly("蓝牙已关闭");
                        mBluetoothAdapter = null;
                        if(BizConstant.IS_BT_KEPP_ON) initBleAdapter();
                        break;
                    }
                    else if(state == BluetoothAdapter.STATE_ON){
                        L.logOnly("蓝牙已打开");
                        checkIfDoNext();
                    }
                    break;
                }

                case BleService.ACTION_GATT_RSSI        : {break;}
                case BleService.ACTION_CHAR_READED      : {break;}
                case BleService.ACTION_DATA_AVAILABLE   : {break;}

                case BleService.ACTION_GATT_CONNECTED: {
                    Toast.makeText(mActivity, "蓝牙设备连接成功", Toast.LENGTH_SHORT).show();
                    new Handler().postDelayed(() -> checkIfDoNext(), 1000);
                    break;
                }

                case BleService.ACTION_GATT_DISCONNECTED: {
                    Toast.makeText(mActivity, "蓝牙设备连接断开", Toast.LENGTH_SHORT).show();

                    if(!BizConstant.IS_BT_KEPP_ON) break;
                    L.logOnly("蓝牙设备连接断开, 开始请求重新连接蓝牙");
                    bleService.connect(mDevice.getAddress());        // 重连
                    break;
                }

                case BizConstant.BROAD_BT_PARING: { // 系统弹出蓝牙配对密码输入框
                    MqttFromS<Action> mActionMqttFromS = getNextTask();
                    if(mActionMqttFromS == null || mActionMqttFromS.data == null || mActionMqttFromS.data.btPairPWD == null) break;
                    BsUtil.doEscapePin(intent, mActionMqttFromS, this);
                    break;
                }
            }
        }
    };

    /** 断开蓝牙 */
    public void disConnect(){
        // 1. 服务断开蓝牙GATT
        // 2. 删除配对关系
        if(bleService != null) {
            bleService.disconnect();
            L.logOnly("请求断开蓝牙");
        }
    }

    /** 清除绑定关系 */
    private void doClearBond(){
        try {
            ClsUtils.removeBond(BluetoothDevice.class, mDevice);
            L.logOnly("删除配对关系");
        } catch (Exception e) {
            L.logOnly(e.getMessage());
        }
    }

    /** 注销监听, 关闭蓝牙 */
    public void onDestroy(){
        L.logOnly("1. 停止监听蓝牙广播, 2. 解绑\"连接服务\", 3. 关闭蓝牙");
        if(mActivity == null) return;
        try{
            if(mConn != null)               mActivity.unbindService(mConn);
            if(bleService != null)          bleService = null;
            if(mBroadcastReceiver != null)  mActivity.unregisterReceiver(mBroadcastReceiver);
        }
        catch (Exception e){ L.logOnly(e.getMessage()); }

        disableBT();
    }

    /** 关掉蓝牙 */
    private void disableBT(){
        if(mBluetoothAdapter != null) {
            mBluetoothAdapter.disable();
            return;
        }

        final BluetoothManager bluetoothManager = (BluetoothManager) mActivity.getSystemService(Context.BLUETOOTH_SERVICE);
        mBluetoothAdapter = bluetoothManager.getAdapter();
        if(mBluetoothAdapter == null) return;
        mBluetoothAdapter.disable();
        mBluetoothAdapter = null;
    }


    /** 发数据 */
    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public void sendMsg(MqttFromS<Action> actionMqttFromS){
        if(actionMqttFromS == null
                || actionMqttFromS.data == null
                || actionMqttFromS.data.cmd == null
                || TextUtils.isEmpty(actionMqttFromS.data.cmd.serviceId)
                || TextUtils.isEmpty(actionMqttFromS.data.cmd.chartId)
                || actionMqttFromS.data.cmd.targetValue == null
        ) return;

        if(mBluetoothAdapter == null){
            TOAST.showShort(mActivity, "111请先初始化蓝牙设备!");
            return;
        }

        if(mDevice == null){
            TOAST.showShort(mActivity, "111未能找到指定的蓝牙设备");
            return;
        }

        if(bleService == null || bleService.mConnectionState != BleService.STATE_CONNECTED) {
            disConnect();
            doClearBond();
            TOAST.showShort(mActivity, "111连接已断开, 请先重连");
            return;
        }

        // 连接蓝牙(对端)提供的服务
        BluetoothGattService gattService = bleService.mBluetoothGatt.getService(UUID.fromString(actionMqttFromS.data.cmd.serviceId));
        if(gattService == null) {
            L.logOnly("未检索到指定服务");
            TOAST.showShort(App.getApp(), "未检索到指定服务");
            return;
        }

        // 连接蓝牙特征值
        BluetoothGattCharacteristic gattChar = gattService.getCharacteristic(UUID.fromString(actionMqttFromS.data.cmd.chartId));
        if(gattChar == null) {
            L.logOnly("未检索到指定特征值");
            TOAST.showShort(App.getApp(), "未检索到指定特征值");
            return;
        }

        actionMqttFromS.data.state = Action.ACTION_SATE_ING;
        for(String targetValue : actionMqttFromS.data.cmd.targetValue){
            L.logOnly("执行指令:" + actionMqttFromS.data.requestName + ", 指令:" + targetValue);
            gattChar.setValue(NumberUtil.str2Byte(targetValue));
            bleService.mBluetoothGatt.writeCharacteristic(gattChar);
        }
        actionMqttFromS.data.state = Action.ACTION_SATE_OK;


//        // 设置Characteristic主动通知, 实时监听Characteristic变化
//        bleService.mBluetoothGatt.setCharacteristicNotification(gattChar, true);
//
//        // 向Characteristic的Descriptor属性写入通知开关，使蓝牙设备主动向手机发送数据
//        BluetoothGattDescriptor descriptor = gattChar.getDescriptor(UUID.randomUUID());// "${DescriptorId}"
//        // descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);//和通知类似,但服务端不主动发数据,只指示客户端读取数据
//        descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
//        bleService.mBluetoothGatt.writeDescriptor(descriptor);

//        // 定制蓝牙广播, 自定义服务等
//        BluetoothLeAdvertiser mBluetoothLeAdvertiser = mBluetoothAdapter.getBluetoothLeAdvertiser();
//        mBluetoothLeAdvertiser.startAdvertising();
    }

    @Override
    public void onWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
        // 检测到成功写入值之后, 将本item从map中删除
        MqttFromS<Action> currActionMqttFromS = MqttFromS.removeTargetAction(mActions, characteristic);
        if(mListener != null) mListener.onOrderFinish(currActionMqttFromS); // 确认成功之后, 通知mqttManager发消息

        checkNormalTaskOrDisBT();  // 检测一下map里是否还有action, 有就接着执行, 没有就把蓝牙关了
    }

    /** 获取蓝牙动态广播的监听 */
    private IntentFilter makeIntentFilter() {
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
        intentFilter.addAction(BleService.ACTION_CHAR_READED);
        intentFilter.addAction(BleService.ACTION_GATT_CONNECTED);
        intentFilter.addAction(BleService.ACTION_GATT_DISCONNECTED);
        intentFilter.addAction(BleService.ACTION_GATT_SERVICES_DISCOVERED);
        intentFilter.addAction(BleService.ACTION_DATA_AVAILABLE);
        intentFilter.addAction(BleService.BATTERY_LEVEL_AVAILABLE);
        intentFilter.addAction(BleService.ACTION_GATT_RSSI);
        intentFilter.addAction(BizConstant.BROAD_BT_PARING);
        return intentFilter;
    }

    /** 检查是否有待执行命令 */
    public boolean isTaskExist() {
        return mActions != null && mActions.size() != 0;
    }

    /** 获取下一个命令 */
    public MqttFromS<Action> getNextTask() {
        return MqttFromS.getFirstFromMap(mActions);
    }

    /** 检查是否有指令, 有就执行, 没有就按配置设定是否关闭蓝牙 */
    public void checkNormalTaskOrDisBT() {
        MqttFromS<Action> nextActionMqttFromS = MqttFromS.getFirstFromMap(mActions);
        if(nextActionMqttFromS == null && !BizConstant.IS_BT_KEPP_ON) {   // 没有就把蓝牙关了
            disConnect();
            doClearBond();
            disableBT();
        }
        else sendMsg(nextActionMqttFromS);   // 有就接着执行
    }

    /** 按是否自每次关闭蓝牙, 以及是否存在待执行任务看要不要继续流程 */
    private void checkIfDoNext(){
        if(!isTaskExist()) {
            if(!BizConstant.IS_BT_KEPP_ON ) disableBT();
            return;
        }
        addAction(getNextTask());
    }

    /** 检查自动关电命令 */
    public void checkUsbOffTask() {
        MqttFromS<Action> mqttFromS = getOffUsbAction();
        if(mqttFromS == null) return;
        if(System.currentTimeMillis() < mqttFromS.targetTime) return;
        L.logOnly("找到自动关电指令, 执行");
        addAction(mqttFromS);
    }
}
